/**
 * Class Jsl.Css - Manipulating with DOM CSS style sheets and rules
 * 
 * @pattern Singleton
 * 
 * @author  Mykhailo Stadnyk <mikhus@gmail.com>
 * @package Jsl
 * @version 1.0
 */
Jsl.Css = Jsl.extend( Object, function() {

	/**
	 * Private members
	 * 
	 * @access private
	 */
	var
		_rules = null,
		_d     = document,
		_re    = /(-[a-z])/gi,
		_fn    = function( m, a) { return a.charAt( 1).toUpperCase(); },
		_head  = _d.getElementsByTagName( 'head')[0] || _d.body
	;

	return {

	/**
	 * Public members
	 */

	/**
	 * Class constructor - following the Jsl rules
	 */
	constructor: function() {
		Jsl.Css.$super.constructor.call( this);
	},

	/**
	 * Creates a new <style> element on a document
	 * 
	 * @param  {String} text - CSS styles definition text
	 * @param  {String} id   - DOM element identifier
	 * @return {Jsl.Css}
	 */
	create : function( text, id){
		var ss, r = _d.createElement( 'style');
		r.setAttribute( 'type', 'text/css');
		if (id)
			r.setAttribute( 'id', id)
		;
		if (Jsl.isIe) {
			_head.appendChild( r);
			ss = r.styleSheet;
			ss.cssText = text;
		} else {
			try {
				r.appendChild( _d.createTextNode( text));
			} catch (e) {
				r.cssText = text;
			}
			_head.appendChild( r);
			ss = r.styleSheet ? r.styleSheet :
				(r.sheet || _d.styleSheets[_d.styleSheets.length - 1])
			;
		}
		this.cache( ss);
		return this;
	},

	/**
	 * Returns existing style sheet element by its identifier
	 * If no style sheet elements existing with such id - returns null
	 * 
	 * @param  {String} id
	 * @return {HTMLStyleElement|HTMLLinkElement}
	 */
	get: function( id) {
		var ss = _d.getElementById( id);

		if (ss.getAttribute( 'type') != 'text/css')
			ss = null
		;

		return ss;
	},

	/**
	 * Removes a style sheet element from a document by its identifier
	 * 
	 * @param  {String} id
	 * @return {Jsl.Css}
	 */
	remove : function( id) {
		var ss = this.get( id);
		ss && ss.parentNode.removeChild( ss);
		return this;
	},

	/**
	 * Creates a new css <link> element
	 * 
	 * @param  {String} id  - identifier of an element
	 * @param  {String} url - URL address of an element
	 * @return {Jsl.Css}
	 */
	createLink : function( id, url) {
		this.remove( id);
		var ss = _d.createElement( 'link');
		ss.setAttribute( 'rel', 'stylesheet');
		ss.setAttribute( 'type', 'text/css');
		ss.setAttribute( 'id', id);
		ss.setAttribute( 'href', url);
		_head.appendChild( ss);
		return this;
	},

	/**
	 * Refreshes rules cache
	 * 
	 * @return {Jsl.Css}
	 */
	refresh : function() {
		this.getRules( true);
		return this;
	},

	/**
	 * Adds a style sheet to local cache
	 * 
	 * @param  {HTMLStyleElement}
	 * @return {Jsl.Css}
	 */
	cache : function( ss){
		if (!_rules) _rules = {};
		try {
			var cssRules = ss.cssRules || ss.rules, j;
			for (j = cssRules.length - 1; j >= 0; --j)
				_rules[cssRules[j].selectorText] = cssRules[j]
			;
		} catch (e) {}
		return this;
	},

	/**
	 * Returns all known CSS rules
	 * 
	 * @param  {Boolean} updCache - [Optional]: flag specifies if required local cache update
	 * @return {Object}
	 */
	getRules : function( updCache){
		if (Jsl.isNull( _rules) || updCache) {
			_rules = {};
			var ds = _d.styleSheets;
			for (var i = 0, s = ds.length; i < s; i++)
				try { this.cache( ds[i]); } catch( e) {}
			;
		}
		return _rules;
	},

	/**
	 * Returns a CSS rule by its selector
	 * 
	 * @param  {String}  selector - CSS selector
	 * @param  {Boolean} updCache - [Optional]: flag specifies if required local cache update
	 * @return {CSSStyleRule}     - CSS rule or null if does not exist
	 */
	getRule : function( selector, updCache) {
		var rs = this.getRules( updCache);
		if (!Jsl.isArray( selector))
			return rs[selector]
		;
		for (var i = 0, s = selector.length; i < s; i++)
			if (rs[selector[i]])
				return rs[selector[i]]
		;
		return null;
	},

	/**
	 * Updates a CSS rule or rules if passed selector is an array of selectors 
	 * 
	 * @param  {String|Array} selector - CSS rule selector
	 * @param  {String} prop           - CSS rule property
	 * @param  {String} val            - CSS rule property value
	 * @return {Boolean}               - true on success, false otherwise
	 */
	updateRule : function( selector, prop, val) {
		if (!Jsl.isArray( selector)) {
			var r = this.getRule( selector);
			if (r) {
				r.style[prop.replace( _re, _fn)] = val;
				return true;
			}
		} else {
			for (var i = 0, s = selector.length; i < s; i++) {
				if (this.updateRule( selector[i], prop, val))
					return true
				;
			}
		}
		return false;
	}

};}());
Jsl.Css = new Jsl.Css(); // singleton
